package org.apache.hadoop.hbase.util;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.yetus.audience.InterfaceAudience;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base class for command lines that start up various HBase daemons.
 */
@InterfaceAudience.Private
public abstract class ServerCommandLine extends Configured implements Tool {
    private static final Logger LOG = LoggerFactory.getLogger(ServerCommandLine.class);
    @SuppressWarnings("serial")
    private static final Set<String> DEFAULT_SKIP_WORDS = new HashSet<String>() {
        {
            add("secret");
            add("passwd");
            add("password");
            add("credential");
        }
    };

    /**
     * Implementing subclasses should return a usage string to print out.
     */
    protected abstract String getUsage();

    /**
     * Print usage information for this command line.
     * @param message if not null, print this message before the usage info.
     */
    protected void usage(String message) {
        if(message != null) {
            System.err.println(message);
            System.err.println("");
        }

        System.err.println(getUsage());
    }

    /**
     * Log information about the currently running JVM.
     */
    public static void logJVMInfo() {
        // Print out vm stats before starting up.
        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        if(runtime != null) {
            LOG.info("vmName=" + runtime.getVmName() + ", vmVendor=" + runtime.getVmVendor() + ", vmVersion=" + runtime.getVmVersion());
            LOG.info("vmInputArguments=" + runtime.getInputArguments());
        }
    }

    /**
     * Print into log some of the important hbase attributes.
     */
    private static void logHBaseConfigs(Configuration conf) {
        final String[] keys = new String[]{
                // Expand this list as you see fit.
                "hbase.tmp.dir", HConstants.HBASE_DIR, HConstants.CLUSTER_DISTRIBUTED, HConstants.ZOOKEEPER_QUORUM,

        };
        for(String key : keys) {
            LOG.info(key + ": " + conf.get(key));
        }
    }

    /**
     * Logs information about the currently running JVM process including
     * the environment variables. Logging of env vars can be disabled by
     * setting {@code "hbase.envvars.logging.disabled"} to {@code "true"}.
     * <p>If enabled, you can also exclude environment variables containing
     * certain substrings by setting {@code "hbase.envvars.logging.skipwords"}
     * to comma separated list of such substrings.
     */
    public static void logProcessInfo(Configuration conf) {

        /**
         *   输出三个非常重要的属性的信息：
         *   1、hbase.rootdir
         *   2、hbase.cluster.distributed
         *   3、hbase.zookeeper.quorum
         */
        logHBaseConfigs(conf);

        // log environment variables unless asked not to
        if(conf == null || !conf.getBoolean("hbase.envvars.logging.disabled", false)) {
            Set<String> skipWords = new HashSet<>(DEFAULT_SKIP_WORDS);
            if(conf != null) {
                String[] confSkipWords = conf.getStrings("hbase.envvars.logging.skipwords");
                if(confSkipWords != null) {
                    skipWords.addAll(Arrays.asList(confSkipWords));
                }
            }

            nextEnv:
            for(Entry<String, String> entry : System.getenv().entrySet()) {
                String key = entry.getKey().toLowerCase(Locale.ROOT);
                String value = entry.getValue().toLowerCase(Locale.ROOT);
                // exclude variables which may contain skip words
                for(String skipWord : skipWords) {
                    if(key.contains(skipWord) || value.contains(skipWord))
                        continue nextEnv;
                }
                LOG.info("env:" + entry);
            }
        }

        // 输出 JVM 的信息
        logJVMInfo();
    }

    /**
     * Parse and run the given command line. This may exit the JVM if
     * a nonzero exit code is returned from <code>run()</code>.
     */
    public void doMain(String args[]) {
        try {
            /*
             *   回到 this 对象的 run() 方法
             *   this = ServerCommandLine
             *   但是 ServerCommandLine 没有  run() 方法，不着急，找他的子类： HMasterCommandLine
             */
            int ret = ToolRunner.run(HBaseConfiguration.create(), this, args); // TODO 核心代码

            if(ret != 0) {
                System.exit(ret);
            }
        } catch(Exception e) {
            LOG.error("Failed to run", e);
            System.exit(-1);
        }
    }
}
